package penjadwalan;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;
public class Scheduler {
    protected ArrayList<Candidate> population = new ArrayList<Candidate>(); // list kromosom
    private Random rand; // generator bilangan random
    private final int POPULATION_SIZE = 10; // ukuran populasi
    private final int PARENT_USE_PERCENT = 10; // persentase sisa kromosom induk yang masih digunakan saat terjadi seleksi alam
    private final int GENOTYPE_SIZE = 15; // jumlah genotip dalam setiap kromosom
    private final float RANDOM_CHANCE_0_1 = 0.5f; // titik tengah bilangan random antara 0 dan 1
    private final int MAX_GENERATION = 50000; // maksimal generasi yang dihasilkan
    public Scheduler() {
        rand = new Random(); 
        // inisialisasi populasi awal
        for (int i = 0; i < POPULATION_SIZE; i++) {
            Candidate kromosom = new Candidate(GENOTYPE_SIZE, RANDOM_CHANCE_0_1, 5);
            kromosom.generateGenotype(); // generate genotipnya
            population.add(kromosom);
        }
        Collections.sort(population); // sorting berdasarkan fitness function
        System.out.println("Init population sorted");
        print();
    }
   protected void print() {
            System.out.println("-- print --");
            for (Candidate kromosom : population) {
                    System.out.println(kromosom);
            }
    }
   protected void generateSchedule() {
        final int maxSteps = MAX_GENERATION;
        int count = 0;
        while (count < maxSteps) {
                count++;
                produceGeneration();
        }
        System.out.println("\nResult");
        print();
    }
    void produceGeneration() {
        ArrayList<Candidate> newPopulation = new ArrayList<Candidate>();
        while (newPopulation.size() < POPULATION_SIZE * (1.0-(PARENT_USE_PERCENT/100.0)) ) {
                int size = population.size();
                int i = rand.nextInt(size);
                int j, k, l;
                j = k = l = i;
                while (j == i)
                        j = rand.nextInt(size);
                while (k == i || k == j)
                        k = rand.nextInt(size);
                while (l == i || l == j || k == l)
                        l = rand.nextInt(size);

                Candidate c1 = population.get(i);
                Candidate c2 = population.get(j);
                Candidate c3 = population.get(k);
                Candidate c4 = population.get(l);

                int f1 = c1.fitnessFunc();
                int f2 = c2.fitnessFunc();
                int f3 = c3.fitnessFunc();
                int f4 = c4.fitnessFunc();

                Candidate w1, w2;

                // proses seleksi awal
                if (f1 > f2)
                        w1 = c1;		
                else 
                        w1 = c2;			
                if (f3 > f4)
                        w2 = c3;			
                else  
                        w2 = c4;
                // calon generasi baru
                Candidate child1, child2;
                
                // proses crossover
                Candidate[] childs = newChilds(w1,w2);
                child1 = childs[0];
                child2 = childs[1];

                // proses mutasi
                double mutatePercent = 0.01;
                boolean m1 = rand.nextFloat() <= mutatePercent;
                boolean m2 = rand.nextFloat() <= mutatePercent;
                if(m1)
                        mutate(child1);
                if(m2)
                        mutate(child2);
                
                // proses seleksi
                boolean isChild1Good = child1.fitnessFunc() >= w1.fitnessFunc();
                boolean isChild2Good = child2.fitnessFunc() >= w2.fitnessFunc();

                newPopulation.add( isChild1Good ? child1 : w1);
                newPopulation.add( isChild2Good ? child2 : w2);
        }

        // ganti kromosom induk yang lama dengan yang baru dengan persentase yang telah ditentukan
        int j = (int)(POPULATION_SIZE*PARENT_USE_PERCENT/100.0);
        for (int i = 0; i < j; i++) {
            newPopulation.add( population.get(i));
        }		
        population=newPopulation;	
        Collections.sort(population);
    }
    // fungsi untuk menghasilkan satu kromosom hasil crossover
    Candidate newChild(Candidate c1, Candidate c2, int pivot)
    {		
            Candidate child = new Candidate(GENOTYPE_SIZE, RANDOM_CHANCE_0_1, 5);

            for (int i = 0; i < pivot; i++) {
                    child.genotype[i] = c1.genotype[i];
            }
            for (int j = pivot; j < GENOTYPE_SIZE; j++) {
                    child.genotype[j] = c2.genotype[j];
            }		

            return child;		
    }

    // fungsi untuk menghasilkan dua kromosom hasil crossover
    Candidate[] newChilds(Candidate c1, Candidate c2)
    {		
            Candidate child1 = new Candidate(GENOTYPE_SIZE, RANDOM_CHANCE_0_1, 5);
            Candidate child2 = new Candidate(GENOTYPE_SIZE, RANDOM_CHANCE_0_1, 5);

            for (int i = 0; i < GENOTYPE_SIZE; i++) {
                    boolean b = rand.nextFloat() >= 0.5;
                    if(b){						
                            child1.genotype[i] = c1.genotype[i];
                            child2.genotype[i] = c2.genotype[i];
                    }
                    else
                    {
                            child1.genotype[i] = c2.genotype[i];
                            child2.genotype[i] = c1.genotype[i];
                    }
            }


            return new Candidate[]{child1,child2} ;		
    }	
    void mutate(Candidate kromosom) {
        int i = rand.nextInt(GENOTYPE_SIZE);
        kromosom.genotype[i] = rand.nextInt(5); // tukar
    }
    public static void main(String[] args) {
        long BEGIN = System.currentTimeMillis();
        Scheduler jadwalGenerator = new Scheduler();
        jadwalGenerator.generateSchedule();
        long END = System.currentTimeMillis();
        System.out.println("Time: " + (END - BEGIN) / 1000.0 + " sec.");
    }
}
